package figuras.malla;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import javax.media.opengl.GL;

import transformaciones.TAfin;
import utils.math.MathFlowers;
import figuras.PuntoVector;

public class Malla {

	protected PuntoVector vertices[];
	protected PuntoVector normales[];
	protected Cara caras[];
	protected int nVertices;
	protected int nNormales;
	protected int nCaras;
	private double yTop;
	private double yBot;
	private double xRight;
	private double xLeft;
	private int texture;
	private boolean blendTexture;

	public void setTexture(int texture) {
		this.texture = texture;
	}

	public Malla() {

	}

	public void dibuja(int modo, GL gl) {
		gl.glEnable(GL.GL_TEXTURE_2D);
		gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
		if(!blendTexture)
			gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_DECAL);
		else
			gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_BLEND);
		for (int i = 0; i < nCaras; i++) {
			gl.glBegin(modo);
			int vertCaras = caras[i].getNumVertices();
			for (int j = 0; j < vertCaras; j++) {
				int iV = caras[i].getIndiceVertice(j);
				int iN = caras[i].getIndiceNormal(j);
				gl.glNormal3d(normales[iN].getX(), normales[iN].getY(),
						normales[iN].getZ());
				// calcular para cada vertice el punto que le corresponde de la
				// textura.
				PuntoVector texel = puntoVectorATexel(vertices[iV]);
				gl.glTexCoord2d(texel.getX(), texel.getY());
				gl.glVertex3d(vertices[iV].getX(), vertices[iV].getY(),
						vertices[iV].getZ());

			}
			gl.glEnd();
		}
		gl.glDisable(GL.GL_TEXTURE_2D);
	}

	public void calculaNormales() {
		for (int i = 0; i < nCaras; i++) {
			normales[i] = caras[i].calculaNormal(vertices, i);
		}
	}

	public PuntoVector[] getVertices() {
		return vertices;
	}

	public void setVertices(PuntoVector[] vertices) {
		this.vertices = vertices;
	}

	public PuntoVector[] getNormales() {
		return normales;
	}

	public void setNormales(PuntoVector[] normales) {
		this.normales = normales;
	}

	public Cara[] getCaras() {
		return caras;
	}

	public void setCaras(Cara[] caras) {
		this.caras = caras;
	}

	public int getNVertices() {
		return nVertices;
	}

	public void setNVertices(int vertices) {
		nVertices = vertices;
	}

	public int getNNormales() {
		return nNormales;
	}

	public void setNNormales(int normales) {
		nNormales = normales;
	}

	public int getNCaras() {
		return nCaras;
	}

	public void setNCaras(int caras) {
		nCaras = caras;
	}

	public void load(BufferedReader in) {
		try {
			this.nVertices = new Integer(in.readLine());
			this.nNormales = new Integer(in.readLine());
			this.nCaras = new Integer(in.readLine());
			vertices = new PuntoVector[nVertices];
			normales = new PuntoVector[nNormales];
			caras = new Cara[nCaras];
			for (int i = 0; i < nVertices; i++) {
				vertices[i] = new PuntoVector(new Double(in.readLine()),
						new Double(in.readLine()), new Double(in.readLine()),
						new Double(in.readLine()));
			}
			for (int i = 0; i < nNormales; i++) {
				normales[i] = new PuntoVector(new Double(in.readLine()),
						new Double(in.readLine()), new Double(in.readLine()),
						new Double(in.readLine()));
			}
			for (int i = 0; i < nCaras; i++) {
				int numVertices = new Integer(in.readLine());
				ArrayList<VerticeNormal> vector = new ArrayList<VerticeNormal>();
				for (int j = 0; j < numVertices; j++) {
					int indiceNormal = new Integer(in.readLine());
					int indiceVertice = new Integer(in.readLine());
					vector.add(new VerticeNormal(indiceVertice, indiceNormal));
				}
				caras[i] = new Cara(numVertices, vector);
			}
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public void load(String s) {
		BufferedReader in;
		try {
			in = new BufferedReader(new FileReader(s));
			load(in);
			in.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void save(String s) {
		try {
			PrintWriter out = new PrintWriter(new BufferedWriter(
					new FileWriter(s)));
			save(out);
			out.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void save(PrintWriter out) {
		out.println(this.nVertices);
		out.println(this.nNormales);
		out.println(this.nCaras);
		for (int i = 0; i < nVertices; i++) {
			out.println(vertices[i].getX());
			out.println(vertices[i].getY());
			out.println(vertices[i].getZ());
			out.println(vertices[i].getPv());
		}
		for (int i = 0; i < nNormales; i++) {
			out.println(normales[i].getX());
			out.println(normales[i].getY());
			out.println(normales[i].getZ());
			out.println(normales[i].getPv());
		}
		for (int i = 0; i < nCaras; i++) {
			out.println(caras[i].getNumVertices());
			for (int j = 0; j < caras[i].getNumVertices(); j++) {
				out.println(caras[i].getIndiceNormal(j));
				out.println(caras[i].getIndiceVertice(j));
			}
		}
	}

	public void saveToAsciiPLY(TAfin matriz, String path, String nameComponent) {
		try {
			PrintWriter out = new PrintWriter(new BufferedWriter(
					new FileWriter(path)));
			writeHeader(out, nameComponent, nVertices, nCaras);
			writeVertex(matriz,out);
			writeFace(out, 0);
			out.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void saveToBinaryPLY(TAfin matriz, String path, String nameComponent) {
		try {
			DataOutputStream out = new DataOutputStream(
					new BufferedOutputStream(new FileOutputStream(path)));
			writeHeader(out, nameComponent, nVertices, nCaras);
			writeVertex(matriz,out);
			writeFace(out, 0);
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void writeHeader(DataOutputStream out, String nameComponent,
			int vertex, int face) throws IOException {
		out.writeBytes("ply\n");
		out.writeBytes("format binary_big_endian 1.0\n");
		out.writeBytes("comment made by " + nameComponent + "\n");
		out.writeBytes("comment this file is a\n");
		out.writeBytes("element vertex " + vertex + "\n");
		out.writeBytes("property float x\n");
		out.writeBytes("property float y\n");
		out.writeBytes("property float z\n");
		out.writeBytes("element face " + face + "\n");
		out.writeBytes("property list int int vertex_index\n");
		out.writeBytes("end_header\n");
	}

	public void writeVertex(TAfin matriz, DataOutputStream out) throws IOException {
		for (int i = 0; i < nVertices; i++) {
			PuntoVector p = MathFlowers.multiplicar(matriz.getMatriz().array(), vertices[i]);
			out.writeDouble(p.getX());
			out.writeDouble(p.getY());
			out.writeDouble(p.getZ());
		}
	}

	public void writeFace(DataOutputStream out, int offset) throws IOException {
		for (int i = 0; i < nCaras; i++) {
			out.writeInt(caras[i].getNumVertices());
			int nVerticesCara = caras[i].getNumVertices();
			for (int j = 0; j < nVerticesCara; j++) {
				out.writeInt(caras[i].getIndiceVertice(j) + offset);
			}
		}
	}

	public static void writeHeader(PrintWriter out, String nameComponent,
			int vertex, int face) {
		out.println("ply");
		out.println("format ascii 1.0");
		out.println("comment made by");
		out.println("comment this file is a " + nameComponent);
		out.println("element vertex " + vertex);
		out.println("property float x");
		out.println("property float y");
		out.println("property float z");
		out.println("element face " + face);
		out.println("property list uchar int vertex_index");
		out.println("end_header");
	}

	public void writeVertex(TAfin matriz,PrintWriter out) {
		System.out.println("write vertex");
		MathFlowers.print(matriz.getMatriz().array());
		for (int i = 0; i < nVertices; i++){
			PuntoVector p = MathFlowers.multiplicar(matriz.getMatriz().array(), vertices[i]);
			out.println((float) p.getX() + " "
					+ (float) p.getY() + " "
					+ (float) p.getZ());
		}
//		for (int i = 0; i < nVertices; i++){
//			out.println((float) vertices[i].getX() + " "
//					+ (float) vertices[i].getY() + " "
//					+ (float) vertices[i].getZ());
//		}
	}

	public void writeFace(PrintWriter out, int offset) {
		for (int i = 0; i < nCaras; i++) {
			int nVerticesCara = caras[i].getNumVertices();
			String cadena = "" + nVerticesCara + " ";
			for (int j = 0; j < nVerticesCara; j++)
				cadena += (caras[i].getIndiceVertice(j) + offset) + " ";
			out.println(cadena);
		}
	}

	public void calculaPuntosDeTextura() {
		for(int i = 0; i < nVertices; i++){
			if(vertices[i].getX()>xRight)
				xRight = vertices[i].getX();
			if(vertices[i].getX()<xLeft)
				xLeft = vertices[i].getX();
			if(vertices[i].getY()<yBot)
				yBot = vertices[i].getY();
			if(vertices[i].getY()>yTop)
				yTop = vertices[i].getY();
		}
	}

	public PuntoVector puntoVectorATexel(PuntoVector p) {
		double y = 1 - ((yTop - p.getY()) / (yTop - yBot));
		double x = 1 - ((xRight - p.getX()) / (xRight - xLeft));
		return new PuntoVector(x, y, 0, 1);
	}

	public double getYTop() {
		return yTop;
	}

	public void setYTop(double top) {
		yTop = top;
	}

	public double getYBot() {
		return yBot;
	}

	public void setYBot(double bot) {
		yBot = bot;
	}

	public double getXRight() {
		return xRight;
	}

	public void setXRight(double right) {
		xRight = right;
	}

	public double getXLeft() {
		return xLeft;
	}

	public void setXLeft(double left) {
		xLeft = left;
	}

	public int getTexture() {
		return texture;
	}

	public boolean isBlendTexture() {
		return blendTexture;
	}

	public void setBlendTexture(boolean blendTexture) {
		this.blendTexture = blendTexture;
	}

	public void trasladar(float size) {
		for(PuntoVector p:vertices){
			p.setY(p.getY()+size);
		}
		
	}

}